#include <linux/module.h>
#include <linux/vmalloc.h>
#include <linux/mm.h>
#include <linux/errno.h>
#include <rdma/uverbs_ioctl.h>

#include "rxe.h"
#include "rxe_loc.h"
#include "rxe_queue.h"

void rxe_mmap_release(struct kref *ref)
{
    struct rxe_mmap_info *ip = container_of(ref, struct rxe_mmap_info, ref);
    struct rxe_dev *rxe = to_rdev(ip->context->device);

    /*遍历链表*/
    spin_lock_bh(&rxe->pending_lock);
    //判断链表是否为空,否则将当前节点删除,
    if(!list_empty(&ip->pending_mmaps))
        list_del(&ip->pending_mmaps);//改变当前删除节点的前驱和后继指针的指向
    spin_unlock_bh(&rxe->pending_lock);

    vfree(ip->obj);
    kfree(ip);
}

/*进程访问vma时调用-对映射的虚拟内存访问时*/
static void rxe_vma_open(struct vm_area_struct *vma)
{
    struct rxe_mmap_info *ip = vma->vm_private_data;

    kref_get(&ip->ref);
}

/*munmap时调用*/
static void rxe_vma_close(struct vm_area_struct *vma)
{
    struct rxe_mmap_info *ip = vma->vm_private_data;

    kref_put(&ip->ref, rxe_mmap_release);
}

static const struct vm_operations_struct rxe_vm_ops = {
    .open = rxe_vma_open,
    .close = rxe_vma_close,
};

int rxe_mmap(struct ib_ucontext *context, struct vm_area_struct *vma)
{
    int ret;
    struct rxe_dev *rxe = to_rdev(context->device);
    unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
    unsigned long size = vma->vm_end - vma->vm_start;
    struct rxe_mmap_info *ip, *pp;

    spin_lock_bh(&rxe->pending_lock);
    /*安全的遍历方式，遍历的同时可以删除节点,从链表里寻找对应的mmap信息*/
    list_for_each_entry_safe(ip, pp, &rxe->pending_mmaps, pending_mmaps) {
        if (context != ip->context || (__u64)offset != ip->info.offset)
            continue;

        /* Don't allow a mmap larger than the object. */
        if (size > ip->info.size) {
            pr_err("mmap region is larger than the object!\n");
            spin_unlock_bh(&rxe->pending_lock);
            ret = -EINVAL;
            goto done;
        }
        goto found;
    }
    /*链表遍历结束了，还没有找到对应的mmap信息*/
    pr_warn("unable to find pending mmap info\n");
    spin_unlock_bh(&rxe->pending_lock);
    ret = -EINVAL;
    goto done;
found:

    /*找到了，这个时候就从链表里删除?*/
    list_del_init(&ip->pending_mmaps);
    spin_unlock_bh(&rxe->pending_lock);

    /*设置虚拟内存和物理内存的映射*/
    ret = remap_vmalloc_range(vma, ip->obj, 0);
    if (ret) {
        pr_err("err %d from remap_vmalloc_range\n", ret);
        goto done;
    }

    /*设置对映射的虚拟内存的操作集*/
    vma->vm_ops = &rxe_vm_ops;
    /*应用程序多次调用mmap时,每个vm_private_data的值以及操作集肯定是不一样的*/
    vma->vm_private_data = ip;
    /*因为一开始mmap时，不会调用open函数，所以这里需要手动调用*/
    rxe_vma_open(vma);
done:
    return ret;
}

struct rxe_mmap_info *rxe_create_mmap_info(struct rxe_dev *rxe, u32 size,
    struct ib_udata *udata, void *obj)
{
    struct rxe_mmap_info *ip;

    if(!udata)
        return ERR_PTR(-EINVAL);

    ip = kmalloc(sizeof(*ip), GFP_KERNEL);
    if(ip)
        return ERR_PTR(-ENOMEM);

    size = PAGE_ALIGN(size);

    spin_lock_bh(&rxe->mmap_offset_lock);
    if (rxe->mmap_offset == 0)
        rxe->mmap_offset = ALIGN(PAGE_SIZE, SHMLBA);//4096

    ip->info.offset = rxe->mmap_offset;
    rxe->mmap_offset += ALIGN(size, SHMLBA);
    spin_unlock_bh(&rxe->mmap_offset_lock);


    INIT_LIST_HEAD(&ip->pending_mmaps);
    /*mmap的大小*/
    ip->info.size = size;
    /*mmap的起始地址*/
    ip->obj = obj;
    kref_init(&ip->ref);
    ip->context = container_of(udata, struct uverbs_attr_bundle, driver_udata)->context;

    return ip;
}
